#ifndef SEM_V2_HPP
#define SEM_V2_HPP

#include <iostream>
#include <memory>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define SEM_CREAT (IPC_CREAT | IPC_EXCL)
#define SEM_GET (IPC_CREAT)
#define EXIT_ERROR(m) \
    do                \
    {                 \
        perror(m);    \
        exit(1);      \
    } while (0)

const std::string path_default = ".";
const int proj_id_default = 0x11;

// 产品类：最终构建的复杂对象
class Sem
{
public:
    Sem(int semid) : _semid(semid)
    {
    }

    ~Sem()
    {
        int ret = semctl(_semid, 0, IPC_RMID);
        if (ret < 0)
        {
            EXIT_ERROR("semctl");
        }
        std::cout << "~Sem() success!" << std::endl;
    }

    void P()
    {
        struct sembuf buf;
        buf.sem_num = 0;
        buf.sem_op = -1;
        buf.sem_flg = SEM_UNDO;
        int ret = semop(_semid, &buf, 1);
        if (ret < 0)
        {
            EXIT_ERROR("semop");
        }
    }

    void V()
    {
        struct sembuf buf;
        buf.sem_num = 0;
        buf.sem_op = 1;
        buf.sem_flg = SEM_UNDO;
        int ret = semop(_semid, &buf, 1);
        if (ret < 0)
        {
            EXIT_ERROR("semop");
        }
    }

private:
    int _semid;
};

// 抽象建造者：定义构建接口
class SemBuilder
{
public:
    virtual ~SemBuilder() = default;
    virtual void SetKey() = 0;
    virtual void SetVal(int val) = 0;
    virtual void SetMode(int mode) = 0;
    virtual void Build(int flag) = 0;
    virtual std::shared_ptr<Sem> GetSem() = 0;
};

// 具体建造者1：游戏电脑建造者
class SemBuilder1 : public SemBuilder
{
public:
    virtual void SetKey() override
    {
        _key = ftok(path_default.c_str(), proj_id_default);
    }

    virtual void SetVal(int val) override
    {
        _val = val;
    }

    virtual void SetMode(int mode) override
    {
        _mode = mode;
    }

    virtual void Build(int flag) override
    {
        int real = _mode == 0 ? flag : flag | _mode;
        int semid = semget(_key, 1, real);
        if (semid < 0)
        {
            EXIT_ERROR("semget");
        }
        std::cout << "sem create success! semid:" << semid << std::endl;
        if (flag == SEM_CREAT)
        {
            Init(semid);
        }
        _sp = std::make_shared<Sem>(semid);
    }

    virtual std::shared_ptr<Sem> GetSem() override
    {
        return _sp;
    }

private:
    void Init(int semid)
    {
        union semun
        {
            int val;               /* Value for SETVAL */
            struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
            unsigned short *array; /* Array for GETALL, SETALL */
            struct seminfo *__buf; /* Buffer for IPC_INFO
                                      (Linux-specific) */
        } sun;
        sun.val = _val;
        int ret = semctl(semid, 0, SETVAL, sun);
        if (ret < 0)
        {
            EXIT_ERROR("semctl");
        }
    }

private:
    int _key;
    int _val;
    int _mode;
    std::shared_ptr<Sem> _sp;
};

// 指挥者：控制构建流程
class SemEngineer
{
public:
    SemEngineer() = default;
    void Construct(std::shared_ptr<SemBuilder> spb,int flag, int mode = 0)
    {
        spb->SetKey();
        spb->SetVal(1);
        spb->SetMode(mode);
        spb->Build(flag);
    }
};

#endif // SEM_V2_HPP